#include "HardwareVertexBuffer.h"
#include "ColourValue.h"
#include "Exception.h"
#include "StringConverter.h"
#include "HardwareBufferManager.h"
#include "DefaultHardwareBufferManager.h"
#include "Root.h"
#include "RenderSystem.h"

namespace PVM
{
	HardwareVertexBuffer::HardwareVertexBuffer(HardwareBufferManagerBase* mgr, size_t vertexSize,
											   size_t numVertices, HardwareBuffer::Usage usage,
											   bool useSystemMemory, bool useShadowBuffer)
	: HardwareBuffer(usage, useSystemMemory, useShadowBuffer),
	mMgr(mgr),
	mNumVertices(numVertices),
	mVertexSize(vertexSize),
	mIsInstanceData(false),
	mInstanceDataStepRate(1)
	{
		mSizeInBytes = mVertexSize * numVertices;
		if (mUseShadowBuffer)
		{
			mpShadowBuffer = new DefaultHardwareVertexBuffer(mMgr, mVertexSize,
															 mNumVertices, HardwareBuffer::HBU_DYNAMIC);
		}
	}
	
	HardwareVertexBuffer::~HardwareVertexBuffer()
	{
		if (mMgr)
		{
			mMgr->_notifyVertexBufferDestroyed(this);
		}
		if (mpShadowBuffer)
		{
			delete mpShadowBuffer;
		}
	}
	
	bool HardwareVertexBuffer::checkIfVertexInstanceDataIsSupported()
	{
		RenderSystem* rs = Root::getSingleton().getRenderSystem();
		return rs->getCapabilities()->hasCapability(RSC_VERTEX_BUFFER_INSTANCE_DATA);
	}
	
	void HardwareVertexBuffer::setIsInstanceData(const bool val)
	{
		if (val && !checkIfVertexInstanceDataIsSupported())
		{
			PVM_EXCEPT("vertex instance data is not supported by then render system.",
					   "HardwareVertexBuffer::checkIfInstanceDataSupported");
		}
		else
		{
			mIsInstanceData = val;
		}
	}
	
	size_t HardwareVertexBuffer::getInstanceDataStepRate() const
	{
		return mInstanceDataStepRate;
	}
	
	void HardwareVertexBuffer::setInstanceDataStepRate(const size_t val)
	{
		if (val > 0)
		{
			mInstanceDataStepRate = val;
		}
		else
		{
			PVM_EXCEPT(
			"Instance data step rate must be bigger than 0.",
			"HardwareVertexBuffer::setInstanceDataStepRate");
		}
	}
	
	VertexElement::VertexElement(unsigned short source, size_t offset,
				VertexElementType theType, VertexElementSemantic semantic, unsigned short index)
	: mSource(source), mOffset(offset), mType(theType),
	mSemantic(semantic), mIndex(index)
	{
	}
	
	size_t VertexElement::getSize(void) const
	{
		return getTypeSize(mType);
	}
	
	size_t VertexElement::getTypeSize(VertexElementType etype)
	{
		switch (etype)
		{
			case VET_COLOUR:
			case VET_COLOUR_ABGR:
			case VET_COLOUR_ARGB:
				return sizeof(RGBA);
			case VET_FLOAT1:
				return sizeof(float);
			case VET_FLOAT2:
				return sizeof(float) * 2;
			case VET_FLOAT3:
				return sizeof(float) * 3;
			case VET_FLOAT4:
				return sizeof(float) * 4;
			case VET_SHORT1:
				return sizeof(short);
			case VET_SHORT2:
				return sizeof(short) * 2;
			case VET_SHORT3:
				return sizeof(short) * 3;
			case VET_SHORT4:
				return sizeof(short) * 4;
			case VET_UBYTE4:
				return sizeof(unsigned char) * 4;
		}
		return 0;
	}
	
	unsigned short VertexElement::getTypeCount(VertexElementType etype)
	{
		switch (etype)
		{
			case VET_COLOUR:
			case VET_COLOUR_ABGR:
			case VET_COLOUR_ARGB:
				return 1;
			case VET_FLOAT1:
				return 1;
			case VET_FLOAT2:
				return 2;
			case VET_FLOAT3:
				return 3;
			case VET_FLOAT4:
				return 4;
			case VET_SHORT1:
				return 1;
			case VET_SHORT2:
				return 2;
			case VET_SHORT3:
				return 3;
			case VET_SHORT4:
				return 4;
			case VET_UBYTE4:
				return 4;
		}
		PVM_EXCEPT("Invalid type",
				   "VertexElement::getTypeCount");
	}
	
	VertexElementType VertexElement::multiplyTypeCount(VertexElementType baseType,
													   unsigned short count)
	{
		switch (baseType)
		{
			case VET_FLOAT1:
				switch (count)
				{
					case 1:
						return VET_FLOAT1;
					case 2:
						return VET_FLOAT2;
					case 3:
						return VET_FLOAT3;
					case 4:
						return VET_FLOAT4;
					default:
						break;
				}
				break;
			case VET_SHORT1:
				switch(count)
				{
					case 1:
						return VET_SHORT1;
					case 2:
						return VET_SHORT2;
					case 3:
						return VET_SHORT3;
					case 4:
						return VET_SHORT4;
					default:
						break;
				}
				break;
			default:
				break;
		}
		PVM_EXCEPT("Invalid base type",
				   "VertexElement::multiplyTypeCount");
	}
	
	VertexElementType VertexElement::getBestColourVertexElementType(void)
	{
		if (Root::getSingletonPtr() && Root::getSingletonPtr()->getRenderSystem())
		{
			return Root::getSingleton().getRenderSystem()->getColourVertexElementType();
		}
		else
		{
			return VET_COLOUR_ABGR;
		}
	}
	
	void VertexElement::convertColourValue(VertexElementType srcType,
		VertexElementType dstType, uint32* ptr)
	{
		if (srcType == dstType)
			return;
		
		*ptr = 
		((*ptr & 0x00FF0000) >> 16) | ((*ptr & 0x000000FF) << 16) | (*ptr & 0xFF00FF00);
	}
	
	uint32 VertexElement::convertColourValue(const ColourValue& src,
											 VertexElementType dst)
	{
		switch (dst)
		{
			case VET_COLOUR_ARGB:
				return src.getAsARGB();
			default:
			case VET_COLOUR_ABGR:
				return src.getAsABGR();
		};
	}
	
	VertexElementType VertexElement::getBaseType(VertexElementType multiType)
	{
		switch (multiType)
		{
			case VET_FLOAT1:
			case VET_FLOAT2:
			case VET_FLOAT3:
			case VET_FLOAT4:
				return VET_FLOAT1;
			case VET_COLOUR:
				return VET_COLOUR;
			case VET_COLOUR_ABGR:
				return VET_COLOUR_ABGR;
			case VET_COLOUR_ARGB:
				return VET_COLOUR_ARGB;
			case VET_SHORT1:
			case VET_SHORT2:
			case VET_SHORT3:
			case VET_SHORT4:
				return VET_SHORT1;
			case VET_UBYTE4:
				return VET_UBYTE4;
		};
		
		return VET_FLOAT1;
	}
	
	VertexDeclaration::VertexDeclaration()
	{
	}
	
	VertexDeclaration::~VertexDeclaration()
	{
	}
	
	const VertexDeclaration::VertexElementList& VertexDeclaration::getElements(void) const
	{
		return mElementList;
	}
	
	const VertexElement& VertexDeclaration::addElement(unsigned short source,
			size_t offset, VertexElementType theType,
			VertexElementSemantic semantic, unsigned short index)
	{
		if (theType == VET_COLOUR)
		{
			theType = VertexElement::getBestColourVertexElementType();
		}
		mElementList.push_back(
		VertexElement(source, offset, theType, semantic, index));
		return mElementList.back();
	}
	
	const VertexElement& VertexDeclaration::insertElement(unsigned short atPosition,
			unsigned short source, size_t offset, VertexElementType theType,
			VertexElementSemantic semantic, unsigned short index)
	{
		if (atPosition >= mElementList.size())
		{
			return addElement(source, offset, theType, semantic, index);
		}
		
		VertexElementList::iterator i = mElementList.begin();
		for (unsigned short n = 0; n < atPosition; ++n)
			++i;
		
		i = mElementList.insert(i,
								VertexElement(source, offset, theType, semantic, index));
		return *i;
	}
	
	const VertexElement* VertexDeclaration::getElement(unsigned short index) const
	{
		assert(index < mElementList.size() && "Index out of bounds");
		
		VertexElementList::const_iterator i = mElementList.begin();
		for (unsigned short n = 0; n < index; ++n)
			++i;
		
		return &(*i);
	}
	
	void VertexDeclaration::removeElement(unsigned short elem_index)
	{
		assert(elem_index < mElementList.size() && "Index out of bounds");
		VertexElementList::iterator i = mElementList.begin();
		for (unsigned short n = 0; n < elem_index; ++n)
			++i;
		mElementList.erase(i);
	}
	
	void VertexDeclaration::removeElement(VertexElementSemantic semantic, unsigned short index)
	{
		VertexElementList::iterator ei, eiend;
		eiend = mElementList.end();
		for (ei = mElementList.begin(); ei != eiend; ++ei)
		{
			if (ei->getSemantic() == semantic && ei->getIndex() == index)
			{
				mElementList.erase(ei);
				break;
			}
		}
	}
	
	void VertexDeclaration::removeAllElements(void)
	{
		mElementList.clear();
	}
	
	void VertexDeclaration::modifyElement(unsigned short elem_index,
										  unsigned short source, size_t offset, VertexElementType theType,
										  VertexElementSemantic semantic, unsigned short index)
	{
		assert(elem_index < mElementList.size() && "Index out of bounds");
		VertexElementList::iterator i = mElementList.begin();
		std::advance(i, elem_index);
		(*i) = VertexElement(source, offset, theType, semantic, index);
	}
	
	const VertexElement* VertexDeclaration::findElementBySemantic(
		VertexElementSemantic sem, unsigned short index) const
	{
		VertexElementList::const_iterator ei, eiend;
		eiend = mElementList.end();
		for (ei = mElementList.begin(); ei != eiend; ++ei)
		{
			if (ei->getSemantic() == sem && ei->getIndex() == index)
			{
				return &(*ei);
			}
		}
		
		return NULL;
	}
	
	VertexDeclaration::VertexElementList VertexDeclaration::findElementsBySource(
	unsigned short source) const
	{
		VertexElementList retList;
		VertexElementList::const_iterator ei, eiend;
		eiend = mElementList.end();
		for (ei = mElementList.begin(); ei != eiend; ++ei)
		{
			if (ei->getSource() == source)
			{
				retList.push_back(*ei);
			}
		}
		return retList;
	}
	
	size_t VertexDeclaration::getVertexSize(unsigned short source) const
	{
		VertexElementList::const_iterator i, iend;
		iend = mElementList.end();
		size_t sz = 0;
		
		for (i = mElementList.begin(); i != iend; ++i)
		{
			if (i->getSource() == source)
			{
				sz += i->getSize();
			}
		}
		
		return sz;
	}
	
	VertexDeclaration* VertexDeclaration::clone(HardwareBufferManagerBase* mgr) const
	{
		HardwareBufferManagerBase* pManager = mgr ? mgr : HardwareBufferManager::getSingletonPtr();
		VertexDeclaration* ret = pManager->createVertexDeclaration();
		
		VertexElementList::const_iterator i, iend;
		iend = mElementList.end();
		for (i = mElementList.begin(); i != iend; ++i)
		{
			ret->addElement(i->getSource(), i->getOffset(), i->getType(), i->getSemantic(), i->getIndex());
		}
		return ret;
	}
	
	bool VertexDeclaration::vertexElementLess(const VertexElement& e1, const VertexElement& e2)
	{
		if (e1.getSource() < e2.getSource())
		{
			return true;
		}
		else if (e1.getSource() == e2.getSource())
		{
			if (e1.getSemantic() < e2.getSemantic())
			{
				return true;
			}
			else if (e1.getSemantic() == e2.getSemantic())
			{
				if (e1.getIndex() < e2.getIndex())
				{
					return true;
				}
			}
		}
		return false;
	}
	void VertexDeclaration::sort(void)
	{
		mElementList.sort(VertexDeclaration::vertexElementLess);
	}
	void VertexDeclaration::closeGapsInSource(void)
	{
		if (mElementList.empty())
			return;
		
		sort();
		
		VertexElementList::iterator i, iend;
		iend = mElementList.end();
		unsigned short targetIdx = 0;
		unsigned short lastIdx = getElement(0)->getSource();
		unsigned short c = 0;
		for (i = mElementList.begin(); i != iend; ++i, ++c)
		{
			VertexElement& elem = *i;
			if (lastIdx != elem.getSource())
			{
				targetIdx++;
				lastIdx = elem.getSource();
			}
			if (targetIdx != elem.getSource())
			{
				modifyElement(c, targetIdx, elem.getOffset(), elem.getType(),
							  elem.getSemantic(), elem.getIndex());
			}
		}
	}
	
	VertexDeclaration* VertexDeclaration::getAutoOrganisedDeclaration(
	bool skeletalAnimation, bool vertexAnimation, bool vertexAnimationNormals) const
	{
		VertexDeclaration* newDecl = this->clone();
		const VertexDeclaration::VertexElementList& elems = newDecl->getElements();
		VertexDeclaration::VertexElementList::const_iterator i;
		unsigned short c = 0;
		for (i = elems.begin(); i != elems.end(); ++i, ++c)
		{
			const VertexElement& elem = *i;
			newDecl->modifyElement(c, 0, 0, elem.getType(), elem.getSemantic(), elem.getIndex());
		}
		newDecl->sort();
		size_t offset = 0;
		c = 0;
		unsigned short buffer = 0;
		VertexElementSemantic prevSemantic = VES_POSITION;
		for (i = elems.begin(); i != elems.end(); ++i, ++c)
		{
			const VertexElement& elem = *i;
			bool splitWithPrev = false;
			bool splitWithNext = false;
			switch (elem.getSemantic())
			{
				case VES_POSITION:
					splitWithPrev = false;
					splitWithNext = vertexAnimation && !vertexAnimationNormals;
					break;
				case VES_NORMAL:
					splitWithPrev = (prevSemantic == VES_BLEND_WEIGHTS || prevSemantic == VES_BLEND_INDICES);
					splitWithNext = (skeletalAnimation || (vertexAnimation && vertexAnimationNormals));
					break;
				case VES_BLEND_WEIGHTS:
					splitWithPrev = true;
					break;
				case VES_BLEND_INDICES:
					splitWithNext = true;
					break;
				default:
				case VES_DIFFUSE:
				case VES_SPECULAR:
				case VES_TEXTURE_COORDINATES:
				case VES_BINORMAL:
				case VES_TANGENT:
					splitWithPrev = prevSemantic == VES_POSITION &&
					(skeletalAnimation || vertexAnimation);
					break;
			}
			
			if (splitWithPrev && offset)
			{
				++buffer;
				offset = 0;
			}
			
			prevSemantic = elem.getSemantic();
			newDecl->modifyElement(c, buffer, offset,
								   elem.getType(), elem.getSemantic(), elem.getIndex());
			
			if (splitWithNext)
			{
				++buffer;
				offset = 0;
			}
			else
			{
				offset += elem.getSize();
			}
		}
		
		return newDecl;
	}
	
	unsigned short  VertexDeclaration::getMaxSource(void) const
	{
		VertexElementList::const_iterator i, iend;
		iend = mElementList.end();
		unsigned short ret = 0;
		for (i = mElementList.begin(); i != iend; ++i)
		{
			if (i->getSource() > ret)
			{
				ret = i->getSource();
			}
		}
		return ret;
	}
	
	unsigned short VertexDeclaration::getNextFreeTextureCoordinate() const
	{
		unsigned short texCoord = 0;
		for (VertexElementList::const_iterator i = mElementList.begin();
			 i != mElementList.end(); ++i)
		{
			const VertexElement& el = *i;
			if (el.getSemantic() == VES_TEXTURE_COORDINATES)
			{
				++texCoord;
			}
		}
		return texCoord;
	}
	
	VertexBufferBinding::VertexBufferBinding() : mHighIndex(0)
	{
	}
	
	VertexBufferBinding::~VertexBufferBinding()
	{
		unsetAllBindings();
	}
	void VertexBufferBinding::setBinding(unsigned short index, const HardwareVertexBufferSharedPtr& buffer)
	{
		mBindingMap[index] = buffer;
		mHighIndex = std::max(mHighIndex, (unsigned short)(index + 1));
	}
	void VertexBufferBinding::unsetBinding(unsigned short index)
	{
		VertexBufferBindingMap::iterator i = mBindingMap.find(index);
		if (i == mBindingMap.end())
		{
			PVM_EXCEPT("Cannot find buffer binding for index " + StringConverter::toString(index),
					   "VertexBufferBinding::unsetBinding");
		}
		mBindingMap.erase(i);
	}
	void VertexBufferBinding::unsetAllBindings(void)
	{
		mBindingMap.clear();
		mHighIndex = 0;
	}
	const VertexBufferBinding::VertexBufferBindingMap&
	VertexBufferBinding::getBindings(void) const
	{
		return mBindingMap;
	}
	const HardwareVertexBufferSharedPtr& VertexBufferBinding::getBuffer(unsigned short index) const
	{
		VertexBufferBindingMap::const_iterator i = mBindingMap.find(index);
		if (i == mBindingMap.end())
		{
			PVM_EXCEPT("No buffer is bound to that index.",
					   "VertexBufferBinding::getBuffer");
		}
		return i->second;
	}
	bool VertexBufferBinding::isBufferBound(unsigned short index) const
	{
		return mBindingMap.find(index) != mBindingMap.end();
	}
	unsigned short VertexBufferBinding::getLastBoundIndex(void) const
	{
		return mBindingMap.empty() ? 0 : mBindingMap.rbegin()->first + 1;
	}
	bool VertexBufferBinding::hasGaps(void) const
	{
		if (mBindingMap.empty())
			return false;
		if (mBindingMap.rbegin()->first + 1 == (int)mBindingMap.size())
			return false;
		return true;
	}
	void VertexBufferBinding::closeGaps(BindingIndexMap& bindingIndexMap)
	{
		bindingIndexMap.clear();
		
		VertexBufferBindingMap newBindingMap;
		VertexBufferBindingMap::const_iterator it;
		ushort targetIndex = 0;
		for (it = mBindingMap.begin(); it != mBindingMap.end(); ++it, ++targetIndex)
		{
			bindingIndexMap[it->first] = targetIndex;
			newBindingMap[targetIndex] = it->second;
		}
		mBindingMap.swap(newBindingMap);
		mHighIndex = targetIndex;
	}
	bool VertexBufferBinding::getHasInstanceData() const
	{
		VertexBufferBinding::VertexBufferBindingMap::const_iterator i, iend;
		iend = mBindingMap.end();
		for (i = mBindingMap.begin(); i != iend; ++i)
		{
			if (i->second->getInstanceData())
			{
				return true;
			}
		}
		return false;
	}
	HardwareVertexBufferSharedPtr::HardwareVertexBufferSharedPtr(HardwareVertexBuffer* buf)
	: SharedPtr<HardwareVertexBuffer>(buf)
	{
	}
}
